use std::collections::HashMap;
use std::sync::OnceLock;

static AREA_CODES: OnceLock<HashMap<String, String>> = OnceLock::new();

pub fn check_id_card(id_card_no: &str) -> String {
    // Basic format check
    if !is_valid_format(id_card_no) {
        return "身份证号码错误".to_string();
    }

    // Convert 15-digit to 18-digit format
    let id_card_no = if id_card_no.len() == 15 {
        convert_to_18_digit(id_card_no)
    } else {
        id_card_no.to_string()
    };

    // Check digit verification
    if !verify_check_code(&id_card_no) {
        return "身份证号码错误".to_string();
    }

    // Parse birth date
    let birth_date = parse_birth_date(&id_card_no);
    if !is_valid_date(&birth_date) {
        return "身份证号码错误".to_string();
    }

    // Get gender
    let gender = if id_card_no.chars().nth(16).unwrap().to_digit(10).unwrap() % 2 == 0 {
        "女"
    } else {
        "男"
    };

    // Get area information
    let area_code = &id_card_no[0..6];
    let area_info = get_area_info(area_code);

    if area_info == "未知地区" {
        return "身份证号码错误".to_string();
    }

    format!("身份证号码正确,{},{},{}", gender, birth_date, area_info)
}

fn is_valid_format(id: &str) -> bool {
    // Check length
    if id.len() != 15 && id.len() != 18 {
        return false;
    }

    // For 15-digit ID, all must be numbers
    if id.len() == 15 {
        return id.chars().all(|c| c.is_ascii_digit());
    }

    // For 18-digit ID
    // First 17 digits must be numbers
    if !id[..17].chars().all(|c| c.is_ascii_digit()) {
        return false;
    }
    // Last digit can be number or 'X'
    let last_char = id.chars().last().unwrap();
    last_char.is_ascii_digit() || last_char == 'X' || last_char == 'x'
}

fn verify_check_code(id: &str) -> bool {
    // Weight factors
    let weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    // Check code corresponding values
    let check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

    let sum: usize = id[..17]
        .chars()
        .enumerate()
        .map(|(i, c)| c.to_digit(10).unwrap() as usize * weights[i])
        .sum();

    let check_code = check_codes[sum % 11];
    check_code.to_ascii_uppercase() == id.chars().last().unwrap().to_ascii_uppercase()
}

fn parse_birth_date(id: &str) -> String {
    let year = &id[6..10];
    let month = &id[10..12];
    let day = &id[12..14];
    format!("{}年{}月{}日", year, month, day)
}

fn is_valid_date(date: &str) -> bool {
    // Extract numeric part
    let year_str: String = date.chars().take_while(|c| c.is_ascii_digit()).collect();
    let month_str: String = date
        .chars()
        .skip_while(|c| c.is_ascii_digit())
        .skip(1) // Skip "年"
        .take_while(|c| c.is_ascii_digit())
        .collect();
    let day_str: String = date
        .chars()
        .skip_while(|c| c.is_ascii_digit())
        .skip(1) // Skip "年"
        .skip_while(|c| c.is_ascii_digit())
        .skip(1) // Skip "月"
        .take_while(|c| c.is_ascii_digit())
        .collect();

    let year = year_str.parse::<i32>().unwrap_or(0);
    let month = month_str.parse::<u32>().unwrap_or(0);
    let day = day_str.parse::<u32>().unwrap_or(0);

    if year < 1900 || year > 2100 {
        return false;
    }
    if month < 1 || month > 12 {
        return false;
    }

    let days_in_month = match month {
        2 => {
            if is_leap_year(year) {
                29
            } else {
                28
            }
        }
        4 | 6 | 9 | 11 => 30,
        _ => 31,
    };

    day >= 1 && day <= days_in_month
}

fn is_leap_year(year: i32) -> bool {
    (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}

fn get_area_info(code: &str) -> String {
    let area_map = AREA_CODES.get_or_init(|| {
        let mut map = HashMap::new();
        if let Ok(content) = std::fs::read_to_string("src/area_codes.json") {
            let content = content.trim().trim_start_matches('{').trim_end_matches('}');
            for line in content.lines() {
                let line = line.trim().trim_end_matches(',');
                if line.is_empty() {
                    continue;
                }
                let parts: Vec<&str> = line.split(':').collect();
                if parts.len() == 2 {
                    let code = parts[0].trim().trim_matches('"').to_string();
                    let area = parts[1].trim().trim_matches('"').to_string();
                    map.insert(code, area);
                }
            }
        }
        map
    });

    let province_code = format!("{}0000", &code[0..2]);
    let city_code = format!("{}00", &code[0..4]);
    let district_code = code.to_string();

    let province = area_map.get(&province_code).map_or("", |s| s);
    let city = area_map.get(&city_code).map_or("", |s| s);
    let district = area_map.get(&district_code).map_or("", |s| s);

    if province.is_empty() || city.is_empty() || district.is_empty() {
        return "未知地区".to_string();
    }

    // 1. Process province name
    let is_municipality = ["北京市", "上海市", "天津市", "重庆市"].contains(&province);
    let province_name = if is_municipality {
        province.trim_end_matches("市")
    } else {
        province.trim_end_matches("省")
    };

    // 2. Process city name
    let city_name = if is_municipality {
        "市辖区"
    } else {
        city.split("市")
            .next()
            .unwrap()
            .trim_start_matches(province)
            .trim_start_matches("省")
    };

    // 3. Process district name
    let is_county = district.ends_with("县");
    let district_name = district
        .split(if is_county { "县" } else { "区" })
        .next()
        .unwrap()
        .split("市")
        .last()
        .unwrap()
        .trim_start_matches(province)
        .trim_start_matches(city);

    // 4. Format the final string
    if is_municipality {
        format!(
            "{}市-{}-{}{}",
            province_name,
            city_name,
            district_name,
            if is_county { "" } else { "区" }
        )
    } else {
        format!(
            "{}省-{}市-{}{}",
            province_name,
            city_name,
            district_name,
            if is_county { "县" } else { "区" }
        )
    }
}

fn convert_to_18_digit(id: &str) -> String {
    // Insert '19' before birth year (assuming all 15-digit IDs are from 19xx)
    let mut new_id = String::with_capacity(18);
    new_id.push_str(&id[0..6]); // Area code
    new_id.push_str("19"); // Century
    new_id.push_str(&id[6..15]); // Rest of the original number

    // Calculate check digit
    let weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    let check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

    let sum: usize = new_id
        .chars()
        .enumerate()
        .map(|(i, c)| c.to_digit(10).unwrap() as usize * weights[i])
        .sum();

    new_id.push(check_codes[sum % 11]);
    new_id
}
